Let's check out the platform we're currently running on

In [ ]:
import platform
print(platform.uname())
uname_result(system='Linux', node='409f01564ddd', release='5.10.104-linuxkit', version='#1 SMP Thu Mar 17 17:08:06 UTC 2022', machine='armv7l')

Hopefully that shows armv7l :) Time to use this code. The example has a simple add() function we can use to check that the package can be found and used.

In [ ]:
import example as m
print(m.add(1, 2))
3

Incredible.

In [ ]:
# print(m.add_quad([0, 1, 2, 3], [-1, -2, 4, 7]))
In [ ]:
from plotly.subplots import make_subplots
import plotly.graph_objects as go

def plot(x, y, **kwargs):
    fig = make_subplots()
    fig.add_trace(go.Scatter(x=x, y=y))
    fig.show()

Time to use the C++ implementation of the Magic Circle to generate a sine wave.

In [ ]:
osc = m.MagicCircle()
sample_rate = 1_000.0 
osc.set_freq(sample_rate / 256, sample_rate)
x = [float(i) for i in range(0, int(sample_rate))]
y = [osc.advance() for _ in x]
plot(x, y)

Here we generate 4 seconds of audio and add a player into this notebook.

In [ ]:
from IPython.display import Audio

sample_rate = 44_100.
osc.reset()
osc.set_freq(440.0, sample_rate)
Audio([osc.advance() for _ in range(0, 4 * int(sample_rate))], rate=sample_rate)
Out[ ]:
Your browser does not support the audio element.

For those who don't have perfect pitch, it might be useful to check out the actual frequency of this sine wave

In [ ]:
import numpy as np

N = 20_000
osc.reset()
osc.set_freq(440.0, float(N))
y = [osc.advance() for _ in range(0, N)]
Y = [np.abs(c) / N for c in np.fft.rfft(y)]
freq = np.arange(len(Y)) * N / (2*len(Y))

plot(freq, Y)

Now let's have a look at the distortion processor. It has a configurable parameter, using plotly sliders we can demo it. We can start by creating a function to generate the sliding graph. In practice, this would be part of some sort of custom plotting package extension.

In [ ]:
def sliding(x, renderer, values):
    fig = make_subplots()
    for v in values:
        fig.add_trace(go.Scatter(visible=False, x=x, y=renderer(v)))
    fig.data[1].visible = True
    steps = []
    for i in range(0, len(fig.data)):
        step = dict(
            method='update',
            args=[{'visible': [False] * len(fig.data)}],
        )
        step['args'][0]['visible'][i] = True
        steps.append(step)
    fig.update_layout(height=400, sliders=[dict(
        active=1,
        pad={'t': 20},
        steps=steps
    )])
    fig.show()

And here is the sliding graph used with the distortion

In [ ]:
dist = m.Distortion()
x = np.linspace(-1., 1., 1_000)
gains = np.linspace(0.5, 6.5, 30)

def render_dist(gain):
    dist.set_gain(gain)
    return [dist.process(i) for i in x]

sliding(x, renderer=render_dist, values=gains)

Now let's distort that sine signal and see how it alters the signal while varying the amount of gain.

In [ ]:
x = np.linspace(0, 4 * int(sample_rate))

def render_with_dist(gain):
    osc.reset()
    osc.set_freq(sample_rate / 16, sample_rate)
    dist.set_gain(gain)
    return [dist.process(osc.advance()) for _ in x]

sliding(x, renderer=render_with_dist, values=gains)
In [ ]:
import numpy as np

def render_freqz_with_dist(gain):
    osc.reset()
    osc.set_freq(440.0, float(N))
    dist.set_gain(gain)
    y = [dist.process(osc.advance()) for _ in range(0, N)]
    return [np.abs(c) / N for c in np.fft.rfft(y)]

sliding(freq, renderer=render_freqz_with_dist, values=gains)

What does this sound like?

In [ ]:
sample_rate = 44_100.
osc.reset()
osc.set_freq(440.0, sample_rate)

out = []
num_samples = 4. * sample_rate
num_samples_per_gain_change = num_samples / len(gains)
for gain in gains:
    dist.set_gain(gain)
    out += [dist.process(osc.advance()) *
            0.5 for _ in range(0, int(num_samples_per_gain_change))]
Audio(out, rate=sample_rate)
Out[ ]:
Your browser does not support the audio element.

Cool beans.